module(...,package.seeall)
require'zdc.typeex'
nodeapi=require'zdc.nodeapi'
sbox=require'zdc.sandbox'
local dfskey_autotype=require'zdc.fieldutil'.dfskey_autotype
local convval=require'zdc.sandbox'.convval
local lpeg = require 'lpeg'
local locale = lpeg.locale();

P,R,S,C,Cc,Ct,Cp,V=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cc,lpeg.Ct,lpeg.Cp,lpeg.V

local function token(id,patt) return Ct(Cc(id)*C(patt)) end

local digit=R('09')

-- range of valid characters after first character of identifier
local idsafe=R('AZ','az','\127\255')+P'_'
local idtkmc=(1 - S "'\r\n\f\\")


-- numbers
local number_sign=S'+-'^-1
local number_decimal=digit^1
local number_hexadecimal =P'0'*S'xX'*R('09','AF','af')^1
local number_float = (digit^1*P'.'*digit^0+P'.'*digit^1)*(S'eE'*number_sign*digit^1)^-1
local number=number_hexadecimal+number_float+number_decimal

local bool=P'true'+P'false'
local boolfalse=P'false'+P'nil'

-- identifiers
local ident=(idsafe*(idsafe+digit)^0)
local identmc=(idtkmc*(idtkmc+digit+S'+-.')^0)

local longstring=#(P'[['+(P'['*P'='^0*P'['))
local longstring=longstring*P(function(input,index)
   local level=input:match('^%[(=*)%[',index)
   if level then
      local _,stop=input:find(']' .. level .. ']', index, true)
      if stop then return stop + 1 end
   end
end)
-- strings
local singlequoted_string = P "'" * ((1 - S "'\r\n\f\\") + (P '\\' * 1)) ^ 0 * "'"
local doublequoted_string = P '"' * ((1 - S '"\r\n\f\\') + (P '\\' * 1)) ^ 0 * '"'
local Pstring = singlequoted_string + doublequoted_string + longstring

local comment = P "--" * longstring + P "--" * (P(1) - P "\n")^0 * (P "\n" + -P(1));

local space = (locale.space + comment)^0;
local function K (k) -- keyword
  return P(k) * -(locale.alnum + P "_");
end

local tableconstructor = P{"tableconstructor",
value = K "nil" +
          K "false" +
          K "true" +
          number +
          Pstring +
          V "tableconstructor" +
          P "(" * space * V "exp" * space * P ")";

binop = K "and" + -- match longest token sequences first
          K "or" +
          P ".." +
          P "<=" +
          P ">=" +
          P "==" +
          P "~=" +
          P "+" +
          P "-" +
          P "*" +
          P "/" +
          P "^" +
          P "%" +
          P "<" +
          P ">";

unop = P "-" +P "#" +K "not";
exp=V "unop" * space * V "exp" + V "value" * (space * V "binop" * space * V "exp")^-1;
    
field = P "[" * space * V "exp" * space * P "]" * space * P "=" *
              space * V "exp" +
          ident * space * P "=" * space * V "exp" +
          V "exp";
fieldsep = P "," + P ";";

fieldlist = V"field" * (space * V"fieldsep"* space * V"field")^0* (space * V"fieldsep")^-1;
                  
tableconstructor = P "{" * space * (V"fieldlist" * space)^-1 * P "}";
}

local Pnodedelta = (1-S(' /\t?'))^1
local Pnodesep = P "/"
Ctk=token('tk',ident+number)
Cauto = token('auto',S('?\t'))
Cargsep = token('argsep',S(' \r')^1)
Cnode = token('node',((Pnodesep^0*Pnodedelta)*(Pnodesep^1*Pnodedelta)^0*Pnodesep^0)+Pnodesep)
Cfield = token('field',Pnodedelta)
Cact = token('act',Pnodedelta)
Csubnode =token('subnode',(Pnodesep^0*Pnodedelta))
Csubnoexist =token('subnoexist',(Pnodesep^0*Pnodedelta))
Cvalstr = token('valstr',Pstring)
--Cvalnum = token('valnum',number)
Cvalid = token('valid',Pnodedelta)
--Cval = Cvalstr+Cvalnum+Cvalid
Cval = Cvalstr+Cvalid
Cmc = token('mc',identmc)
Ctable=token('table',tableconstructor)
Cboolfalse = token('boolfalse',boolfalse)
Carg=Cboolfalse+Ctable+Cvalstr+Cvalid

function Cautov(name)
	return Ct(Cc(name)*Cc('')*C(S('\t?')))*Ct(Cc('auto')*Cc('\t'))
end
--[[
local Mset=Ct(Cautov('node')+Cnode*(Cauto+(Cargsep*(Cautov('field')+(Cfield*(Cauto+(Cargsep*(Cautov('val')+Cval))))))))
local Mcreate=Ct(Cautov('node')+Cnode*(Cauto+(Cargsep*(Cautov('subnode')+(Csubnode*(Cauto+(Cargsep*(Cautov('mc')+Cmc))^-1))))))


local table_of_tokens = Mset

--local input='/abc/1.2/o abc \t'
local input='/as/a\t'

local tokens = lpeg.match(table_of_tokens, input)

local function print(tokens)
   local print, format = _G.print, _G.string.format
   for _, tk in pairs(tokens) do
      print(tk[1], tk[2])
   end
end
print(tokens)
--]]

local function _matchids(tab,cmd)
	local m={}
	for k,v in ipairs(tab) do
		if string.find(v,cmd,1,true)==1 then
			m[#m+1]=v
		end
	end
	for k,v in pairs(tab) do
		if type(k)~='number' then
			if string.find(k,cmd,1,true)==1 then
				m[#m+1]=k
			end
		end
	end
	local l=#m
	table.sort(m)
	if l==0 then
		return nil,nil,nil
	elseif l==1 then
		return m[1],nil
	else
		return nil,m
	end
end
function _get_field_define_autotype(n,key)
	if not n then return nil,'unknow node' end
	local fd=n._fds[key]
	if not fd then
		fd,key=dfskey_autotype(n._dfs,key)
	end
	return fd,key
end
-- return replace(t)
function parseresult(dal,env,tk)
	local pa = dal:GetNode(env._curlvl)
	if env._curlvl~='' and not pa then return -1,[[can't find node ']] .. env._curlvl .."'" end
	local rt={}
	local lastnode=pa;
	local lastfd
	local lastcd
	local i=1
	while tk[i] do
		local k=tk[i]
		if k[1]=='argsep' then
		elseif k[1]=='node' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local parent_id, rest = string.match(k[2], "^(.+)/(.-)$")
				if not parent_id then parent_id='';rest = k[2] end
				local bn
				if pa then
					bn=nodeapi.seek(pa,parent_id)
				else
					bn=dal:GetNode(parent_id)
				end
				local ma={}
				if bn then
					local luck,sel=_matchids(bn:__get_child_key_list(),rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck and #((bn/rest):__get_child_key_list())==0 then
						return 1," "
					end
					if luck then 
						ma[#ma+1]='<space>'
						ma[#ma+1]='/'
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				return -1,"can't support auto complete"
			end
			local nd
			if not pa then
				nd=dal:GetNode(k[2])
				--print(nd,'get',k[2])
				if not nd then return -1,[[can't find node ]] .. k[2] end
			else
				nd=nodeapi.seek(pa,k[2])
				if not nd then return -1,[[can't find node ]] .. k[2] end
			end
			rt[#rt+1]=nd
			lastnode=nd
		elseif k[1]=='field' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local bn=lastnode
				local ma={}
				if bn then
					local rest=k[2]
					local luck,sel=_matchids(bn:__get_other_fields(bn:__get_ordered_fields_name_lvl()),rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				return -1,'not support auto complete'
			end
			local _fd,nkey=_get_field_define_autotype(lastnode,k[2])
			if not _fd then return -1, 'no type define for ' .. k[2] end
			--rt[#rt+1]=k[2]
			rt[#rt+1]=nkey
			lastfd=_fd
		elseif k[1]=='subnode' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local bn=lastnode
				local ma={}
				if bn then
					local rest=k[2]
					local luck,sel=_matchids(bn:__get_child_key_list(),rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				return -1,'not support auto complete'
			end
			local _cd=lastnode:__get_child_define(k[2])
			if not _cd then return -1, 'no subnode define for ' .. k[2] end
			rt[#rt+1]=k[2]
			lastcd=_cd
		elseif k[1]=='subnoexist' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local bn=lastnode
				local ma={}
				if bn then
					local rest=k[2]
					local luck,sel=_matchids(bn:__get_childiddef_noexist(),rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				return -1,'not support auto complete'
			end
			local _cd=lastnode and lastnode:__get_child_define(k[2])
			if not _cd then return -1, 'no subnode define for ' .. k[2] end
			rt[#rt+1]=k[2]
			lastcd=_cd
		elseif k[1]=='valstr' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local rest=k[2]
				local bn=lastfd
				if bn and bn[2] then
					local luck,sel=zdc.typeex.GetTypeHelp(bn[2],bn[3],rest)
					if luck and luck~=rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					return 2,sel
				end
				return -1,'not support auto complete'
			end
			rt[#rt+1]=convval(k[2])
		elseif k[1]=='boolfalse' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local rest=k[2]
				local bn=lastfd
				if bn and bn[2] then
					local luck,sel=zdc.typeex.GetTypeHelp(bn[2],bn[3],rest)
					if luck and luck~=rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					return 2,sel
				end
				return -1,'not support auto complete'
			end
			rt[#rt+1]=convval(k[2])
		elseif k[1]=='val' or k[1]=='valid' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local rest=k[2]
				local bn=lastfd
				if bn and bn[2] and bn.proto ~= 'sub' then
					local luck,sel=zdc.typeex.GetTypeHelp(bn[2],bn[3],rest)
					if luck and luck~=rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					return 2,sel
				end
				return -1,'not support auto complete'
			end
			rt[#rt+1]=k[2]
--[[
		elseif k[1]=='valnum' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local bn=lastfd
				if bn then
					return 2,{zdc.typeex.GetTypeHelp(bn[2],bn[3])}
				end
				return -1,'not support auto complete'
			end
			rt[#rt+1]=tonumber(k[2])
--]]			
		elseif k[1]=='mc' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				local rest=k[2]
				--auto complete or help and return
				local bn=lastcd
				local ma={}
				if bn and type(bn)=='table' then
					local luck,sel=_matchids(bn._mc,rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				if bn and #rest==0 then
					return 2,ma
				end
				return -1,'not support auto complete'
			end
			rt[#rt+1]=k[2]
		elseif k[1]=='table' then
			if tk[i+1] and tk[i+1][1]=='auto' then				
				return -1,'not support auto complete'
			end
			local dft=convval(k[2])
			if type(dft)=='table' then
				rt[#rt+1]=dft
			end
		elseif k[1]=='act' then
			if tk[i+1] and tk[i+1][1]=='auto' then
				--auto complete or help and return
				local bn=lastnode
				local ma={}
				if bn then
					local rest=k[2]
					local luck,sel=_matchids(bn:__get_acts(),rest)
					if luck and luck~= rest then
						return 1,luck:sub(#rest+1)
					end
					if luck then
						return 1," "
					end
					if sel and #sel>0 then
						for _,s in ipairs(sel) do
							ma[#ma+1]=s
						end
					end
					return 2,ma
				end
				return -1,'not support auto complete'
			end
			local _fd=lastnode and lastnode:__get_act_define(k[2])
			if not _fd then return -1, 'no action define for ' .. k[2] end
			rt[#rt+1]=k[2]
			lastfd=_fd
		else
			if tk[i+1] and tk[i+1][1]=='auto' then
				return 1,""
			end
			rt[#rt+1]=k[2]
		--else
		--	return -1,'command internal error:' .. k[1]
		end
		i=i+1
	end
	return 0,rt
end

